﻿@using BlazorWebAppEFCore.Grid
@using System.Timers;
@inject IContactFilters Filters
@implements IDisposable

Filter by:
<input type="text" autofocus @bind-value="FilterText" @bind-value:event="oninput" />
<button class="btn btn-danger" @onclick="@(() => FilterText = string.Empty)">Clear</button>
Filter on: <select @bind="SelectedColumn">
    @foreach (ContactFilterColumns column in (ContactFilterColumns[])Enum.GetValues(typeof(ContactFilterColumns)))
    {
        <option @attributes="Selected(column)" value="@((int)column)">@(column.ToString())</option>
    }
</select>

@code {
    // Get a reference to the global GridWrapper.
    [CascadingParameter]
    public GridWrapper? Wrapper { get; set; }

    // Wait period in (ms) after the user stops typing.
    const int DebounceMs = 300;

    // Timer for debounce.
    Timer? timer;

    // When ready.
    protected override void OnInitialized()
    {
        // Grab filter.
        filterText = Filters.FilterText;

        // Grab column to filter on.
        selectedColumn = (int)(Filters.FilterColumn);

        base.OnInitialized();
    }

    // Sets selected attribute.
    // column: The ContactFilterColumns being evaluated.
    // Returns the proper attribute to select the option.
    private IEnumerable<KeyValuePair<string, object>> Selected(ContactFilterColumns column)
    {
        if ((int)column == selectedColumn)
        {
            return new[] { new KeyValuePair<string, object>("selected", (object)"selected") };
        }
        return Enumerable.Empty<KeyValuePair<string, object>>();
    }

    private int selectedColumn;

    // Column to filter on.
    private int SelectedColumn
    {
        get => selectedColumn;
        set
        {
            if (value != selectedColumn)
            {
                selectedColumn = value;
                Filters.FilterColumn = (ContactFilterColumns)selectedColumn;
                FilterText = string.Empty;
            }
        }
    }

    private string? filterText;

    // Text to filter on.
    private string? FilterText
    {
        get => filterText;
        set
        {
            if (value != filterText)
            {
                filterText = value;
                // More text means restart the debounce timer.
                timer?.Dispose();
                timer = new(DebounceMs);
                timer.Elapsed += NotifyTimerElapsed;
                timer.Enabled = true;
            }
        }
    }

    // Fired after debounce time.
    // sender: Timer
    // e: Event args
    private async void NotifyTimerElapsed(object? sender, ElapsedEventArgs e)
    {
        timer?.Dispose();
        timer = null;
        if (Filters.FilterText != filterText)
        {
            // Notify the controls.
            Filters.FilterText = filterText?.Trim();

            if (Wrapper is not null)
            {
                await InvokeAsync(() => Wrapper.FilterChanged.InvokeAsync(this));
            }
        }
    }

    // Disposable pattern - override to dispose any ticking timers.
    // disposing is True when disposing.
    public void Dispose()
    {
        timer?.Dispose();
        timer = null;
    }
}
